Completed
Push — master ( 7b524c...0d3c7c )
by Ruben de
05:15 queued 02:24
created

APIClient.mnemonicToSeedHex   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
nc 3
dl 0
loc 18
rs 9.4285
nop 2

2 Functions

Rating   Name   Duplication   Size   Complexity  
A 0 3 1
A 0 3 1
1
var _ = require('lodash'),
2
    q = require('q'),
3
    bitcoin = require('bitcoinjs-lib'),
4
    bitcoinMessage = require('bitcoinjs-message'),
5
6
    bip39 = require("bip39"),
7
    Wallet = require('./wallet'),
8
    RestClient = require('./rest_client'),
9
    Encryption = require('./encryption'),
10
    KeyDerivation = require('./keyderivation'),
11
    EncryptionMnemonic = require('./encryption_mnemonic'),
12
    blocktrail = require('./blocktrail'),
13
    randomBytes = require('randombytes'),
14
    CryptoJS = require('crypto-js'),
15
    webworkifier = require('./webworkifier');
16
17
var useWebWorker = require('./use-webworker')();
18
19
/**
20
 * Bindings to conssume the BlockTrail API
21
 *
22
 * @param options       object{
23
 *                          apiKey: 'API_KEY',
24
 *                          apiSecret: 'API_SECRET',
25
 *                          host: 'defaults to api.blocktrail.com',
26
 *                          network: 'BTC|LTC',
27
 *                          testnet: true|false
28
 *                      }
29
 * @constructor
30
 */
31
var APIClient = function(options) {
32
    var self = this;
33
34
    // handle constructor call without 'new'
35
    if (!(this instanceof APIClient)) {
36
        return new APIClient(options);
37
    }
38
39
    // BLOCKTRAIL_SDK_API_ENDPOINT overwrite for development
40
    if (process.env.BLOCKTRAIL_SDK_API_ENDPOINT) {
41
        options.host = process.env.BLOCKTRAIL_SDK_API_ENDPOINT;
42
    }
43
44
    // trim off leading https?://
45
    if (options.host && options.host.indexOf("https://") === 0) {
46
        options.https = true;
47
        options.host = options.host.substr(8);
48
    } else if (options.host && options.host.indexOf("http://") === 0) {
49
        options.https = false;
50
        options.host = options.host.substr(7);
51
    }
52
53
    if (typeof options.https === "undefined") {
54
        options.https = true;
55
    }
56
57
    if (!options.host) {
58
        options.host = 'api.blocktrail.com';
59
    }
60
61
    if (!options.port) {
62
        options.port = options.https ? 443 : 80;
63
    }
64
65
    self.testnet = options.testnet = options.testnet || false;
66
    if (self.testnet) {
67
        self.network = bitcoin.networks.testnet;
68
    } else {
69
        self.network = bitcoin.networks.bitcoin;
70
    }
71
72
    self.bitcoinCash = options.network && options.network === "BCC";
73
74
    if (!options.endpoint) {
75
        options.endpoint = "/" + (options.apiVersion || "v1") + "/" + (self.testnet ? "t" : "") + (options.network || 'BTC').toUpperCase();
76
    }
77
78
    /**
79
     * @type RestClient
80
     */
81
    self.client = new RestClient(options);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
82
};
83
84
var determineDataStorageV2_3 = function(options) {
85
    return q.when(options)
86
        .then(function(options) {
87
            // legacy
88
            if (options.storePrimaryMnemonic) {
89
                options.storeDataOnServer = options.storePrimaryMnemonic;
90
            }
91
92
            // storeDataOnServer=false when primarySeed is provided
93
            if (typeof options.storeDataOnServer === "undefined") {
94
                options.storeDataOnServer = !options.primarySeed;
95
            }
96
97
            return options;
98
        });
99
};
100
101
var produceEncryptedDataV2 = function(options, notify) {
102
    return q.when(options)
103
        .then(function(options) {
104
            if (options.storeDataOnServer) {
105
                if (!options.secret) {
106
                    if (!options.passphrase) {
107
                        throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
108
                    }
109
110
                    notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
111
112
                    options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
113
                    options.encryptedSecret = CryptoJS.AES.encrypt(options.secret, options.passphrase).toString(CryptoJS.format.OpenSSL); // 'base64' string
114
                }
115
116
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
117
118
                options.encryptedPrimarySeed = CryptoJS.AES.encrypt(options.primarySeed.toString('base64'), options.secret)
119
                    .toString(CryptoJS.format.OpenSSL); // 'base64' string
120
                options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8).toString('hex'); // string because we use it as passphrase
121
122
                notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
123
124
                options.recoveryEncryptedSecret = CryptoJS.AES.encrypt(options.secret, options.recoverySecret)
125
                                                              .toString(CryptoJS.format.OpenSSL); // 'base64' string
126
            }
127
128
            return options;
129
        });
130
};
131
132
APIClient.prototype.promisedEncrypt = function(pt, pw, iter) {
133
    if (useWebWorker) {
134
        // generate randomness outside of webworker because many browsers don't have crypto.getRandomValues inside webworkers
135
        var saltBuf = Encryption.generateSalt();
136
        var iv = Encryption.generateIV();
137
138
        return webworkifier.workify(APIClient.prototype.promisedEncrypt, function() {
139
            return require('./webworker');
140
        }, {
141
            method: 'Encryption.encryptWithSaltAndIV',
142
            pt: pt,
143
            pw: pw,
144
            saltBuf: saltBuf,
145
            iv: iv,
146
            iterations: iter
147
        })
148
            .then(function(data) {
149
                return Buffer.from(data.cipherText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
150
            });
151
    } else {
152
        try {
153
            return q.when(Encryption.encrypt(pt, pw, iter));
154
        } catch (e) {
155
            return q.reject(e);
156
        }
157
    }
158
};
159
160
APIClient.prototype.promisedDecrypt = function(ct, pw) {
161
    if (useWebWorker) {
162
        return webworkifier.workify(APIClient.prototype.promisedDecrypt, function() {
163
            return require('./webworker');
164
        }, {
165
            method: 'Encryption.decrypt',
166
            ct: ct,
167
            pw: pw
168
        })
169
            .then(function(data) {
170
                return Buffer.from(data.plainText.buffer);
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
171
            });
172
    } else {
173
        try {
174
            return q.when(Encryption.decrypt(ct, pw));
175
        } catch (e) {
176
            return q.reject(e);
177
        }
178
    }
179
};
180
181
APIClient.prototype.produceEncryptedDataV3 = function(options, notify) {
182
    var self = this;
183
184
    return q.when(options)
185
        .then(function(options) {
186
            if (options.storeDataOnServer) {
187
                return q.when()
188
                    .then(function() {
189
                        if (!options.secret) {
190
                            if (!options.passphrase) {
191
                                throw new blocktrail.WalletCreateError("Can't encrypt data without a passphrase");
192
                            }
193
194
                            notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET);
195
196
                            // -> now a buffer
197
                            options.secret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
198
199
                            // -> now a buffer
200
                            return self.promisedEncrypt(options.secret, new Buffer(options.passphrase), KeyDerivation.defaultIterations)
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
201
                                .then(function(encryptedSecret) {
202
                                    options.encryptedSecret = encryptedSecret;
203
                                });
204
                        } else {
205
                            if (!(options.secret instanceof Buffer)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if !(options.secret instanceof Buffer) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
206
                                throw new Error('Secret must be a buffer');
207
                            }
208
                        }
209
                    })
210
                    .then(function() {
211
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY);
212
213
                        return self.promisedEncrypt(options.primarySeed, options.secret, KeyDerivation.subkeyIterations)
214
                            .then(function(encryptedPrimarySeed) {
215
                                options.encryptedPrimarySeed = encryptedPrimarySeed;
216
                            });
217
                    })
218
                    .then(function() {
219
                        // skip generating recovery secret when explicitly set to false
220
                        if (options.recoverySecret === false) {
221
                            return;
222
                        }
223
224
                        notify(APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY);
225
                        if (!options.recoverySecret) {
226
                            options.recoverySecret = randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
227
                        }
228
229
                        return self.promisedEncrypt(options.secret, options.recoverySecret, KeyDerivation.defaultIterations)
230
                            .then(function(recoveryEncryptedSecret) {
231
                                options.recoveryEncryptedSecret = recoveryEncryptedSecret;
232
                            });
233
                    })
234
                    .then(function() {
235
                        return options;
236
                    });
237
            } else {
238
                return options;
239
            }
240
        });
241
};
242
243
var doRemainingWalletDataV2_3 = function(options, network, notify) {
244
    return q.when(options)
245
        .then(function(options) {
246
            if (!options.backupPublicKey) {
247
                options.backupSeed = options.backupSeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
248
            }
249
250
            notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
251
252
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
253
254
            notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
255
256
            if (!options.backupPublicKey) {
257
                options.backupPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.backupSeed, network);
258
                options.backupPublicKey = options.backupPrivateKey.neutered();
259
            }
260
261
            options.primaryPublicKey = options.primaryPrivateKey.deriveHardened(options.keyIndex).neutered();
262
263
            notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
264
265
            return options;
266
        });
267
};
268
269
APIClient.prototype.mnemonicToPrivateKey = function(mnemonic, passphrase, cb) {
270
    var self = this;
271
272
    var deferred = q.defer();
273
    deferred.promise.spreadNodeify(cb);
274
275
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
276
277
    deferred.resolve(q.fcall(function() {
278
        return self.mnemonicToSeedHex(mnemonic, passphrase).then(function(seedHex) {
279
            return bitcoin.HDNode.fromSeedHex(seedHex, network);
280
        });
281
    }));
282
283
    return deferred.promise;
284
};
285
286
APIClient.prototype.mnemonicToSeedHex = function(mnemonic, passphrase) {
287
    var self = this;
288
289
    if (useWebWorker) {
290
        return webworkifier.workify(self.mnemonicToSeedHex, function() {
291
            return require('./webworker');
292
        }, {method: 'mnemonicToSeedHex', mnemonic: mnemonic, passphrase: passphrase})
293
            .then(function(data) {
294
                return data.seed;
295
            });
296
    } else {
297
        try {
298
            return q.when(bip39.mnemonicToSeedHex(mnemonic, passphrase));
299
        } catch (e) {
300
            return q.reject(e);
301
        }
302
    }
303
};
304
305
APIClient.prototype.resolvePrimaryPrivateKeyFromOptions = function(options, cb) {
306
    var self = this;
307
308
    var deferred = q.defer();
309
    deferred.promise.nodeify(cb);
310
311
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
312
313
    try {
314
        // avoid conflicting options
315
        if (options.passphrase && options.password) {
316
            throw new blocktrail.WalletCreateError("Can't specify passphrase and password");
317
        }
318
        // normalize passphrase/password
319
        options.passphrase = options.passphrase || options.password;
320
        delete options.password;
321
322
        // avoid conflicting options
323
        if (options.primaryMnemonic && options.primarySeed) {
324
            throw new blocktrail.WalletInitError("Can only specify one of; Primary Mnemonic or Primary Seed");
325
        }
326
327
        // avoid deprecated options
328
        if (options.primaryPrivateKey) {
329
            throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
330
        }
331
332
        // make sure we have at least one thing to use
333
        if (!options.primaryMnemonic && !options.primarySeed) {
334
            throw new blocktrail.WalletInitError("Need to specify at least one of; Primary Mnemonic or Primary Seed");
335
        }
336
337
        if (options.primarySeed) {
338
            self.primarySeed = options.primarySeed;
339
            options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(self.primarySeed, network);
340
            deferred.resolve(options);
341
        } else {
342
            if (!options.passphrase) {
343
                throw new blocktrail.WalletInitError("Can't init wallet with Primary Mnemonic without a passphrase");
344
            }
345
346
            self.mnemonicToSeedHex(options.primaryMnemonic, options.passphrase)
347
                .then(function(seedHex) {
348
                    try {
349
                        options.primarySeed = new Buffer(seedHex, 'hex');
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
350
                        options.primaryPrivateKey = bitcoin.HDNode.fromSeedBuffer(options.primarySeed, network);
351
                        deferred.resolve(options);
352
                    } catch (e) {
353
                        deferred.reject(e);
354
                    }
355
                }, function(e) {
356
                    deferred.reject(e);
357
                });
358
        }
359
    } catch (e) {
360
        deferred.reject(e);
361
    }
362
363
    return deferred.promise;
364
};
365
366
APIClient.prototype.resolveBackupPublicKeyFromOptions = function(options, cb) {
367
    var self = this;
368
369
    var deferred = q.defer();
370
    deferred.promise.nodeify(cb);
371
372
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
373
374
    try {
375
        // avoid conflicting options
376
        if (options.backupMnemonic && options.backupPublicKey) {
377
            throw new blocktrail.WalletInitError("Can only specify one of; Backup Mnemonic or Backup PublicKey");
378
        }
379
380
        // make sure we have at least one thing to use
381
        if (!options.backupMnemonic && !options.backupPublicKey) {
382
            throw new blocktrail.WalletInitError("Need to specify at least one of; Backup Mnemonic or Backup PublicKey");
383
        }
384
385
        if (options.backupPublicKey) {
386
            if (options.backupPublicKey instanceof bitcoin.HDNode) {
387
                deferred.resolve(options);
388
            } else {
389
                options.backupPublicKey = bitcoin.HDNode.fromBase58(options.backupPublicKey, network);
390
                deferred.resolve(options);
391
            }
392
        } else {
393
            self.mnemonicToPrivateKey(options.backupMnemonic, "").then(function(backupPrivateKey) {
394
                options.backupPublicKey = backupPrivateKey.neutered();
395
                deferred.resolve(options);
396
            }, function(e) {
397
                deferred.reject(e);
398
            });
399
        }
400
    } catch (e) {
401
        deferred.reject(e);
402
    }
403
404
    return deferred.promise;
405
};
406
407
APIClient.prototype.debugAuth = function(cb) {
408
    var self = this;
409
410
    return self.client.get("/debug/http-signature", null, true, cb);
411
};
412
413
/**
414
 * get a single address
415
 *
416
 * @param address      string       address hash
417
 * @param [cb]          function    callback function to call when request is complete
418
 * @return q.Promise
419
 */
420
APIClient.prototype.address = function(address, cb) {
421
    var self = this;
422
423
    return self.client.get("/address/" + address, null, cb);
424
};
425
426
APIClient.prototype.addresses = function(addresses, cb) {
427
    var self = this;
428
429
    return self.client.post("/address", null, {"addresses": addresses}, cb);
430
};
431
432
/**
433
 * get all transactions for an address (paginated)
434
 *
435
 * @param address       string      address hash
436
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
437
 * @param [cb]          function    callback function to call when request is complete
438
 * @return q.Promise
439
 */
440
APIClient.prototype.addressTransactions = function(address, params, cb) {
441
    var self = this;
442
443
    if (typeof params === "function") {
444
        cb = params;
445
        params = null;
446
    }
447
448
    return self.client.get("/address/" + address + "/transactions", params, cb);
449
};
450
451
/**
452
 * get all transactions for a batch of addresses (paginated)
453
 *
454
 * @param addresses     array       address hashes
455
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
456
 * @param [cb]          function    callback function to call when request is complete
457
 * @return q.Promise
458
 */
459
APIClient.prototype.batchAddressHasTransactions = function(addresses, params, cb) {
460
    var self = this;
461
462
    if (typeof params === "function") {
463
        cb = params;
464
        params = null;
465
    }
466
467
    return self.client.post("/address/has-transactions", params, {"addresses": addresses}, cb);
468
};
469
470
/**
471
 * get all unconfirmed transactions for an address (paginated)
472
 *
473
 * @param address       string      address hash
474
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
475
 * @param [cb]          function    callback function to call when request is complete
476
 * @return q.Promise
477
 */
478
APIClient.prototype.addressUnconfirmedTransactions = function(address, params, cb) {
479
    var self = this;
480
481
    if (typeof params === "function") {
482
        cb = params;
483
        params = null;
484
    }
485
486
    return self.client.get("/address/" + address + "/unconfirmed-transactions", params, cb);
487
};
488
489
/**
490
 * get all unspent outputs for an address (paginated)
491
 *
492
 * @param address       string      address hash
493
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
494
 * @param [cb]          function    callback function to call when request is complete
495
 * @return q.Promise
496
 */
497
APIClient.prototype.addressUnspentOutputs = function(address, params, cb) {
498
    var self = this;
499
500
    if (typeof params === "function") {
501
        cb = params;
502
        params = null;
503
    }
504
505
    return self.client.get("/address/" + address + "/unspent-outputs", params, cb);
506
};
507
508
/**
509
 * get all unspent outputs for a batch of addresses (paginated)
510
 *
511
 * @param addresses     array       address hashes
512
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
513
 * @param [cb]          function    callback function to call when request is complete
514
 * @return q.Promise
515
 */
516
APIClient.prototype.batchAddressUnspentOutputs = function(addresses, params, cb) {
517
    var self = this;
518
519
    if (typeof params === "function") {
520
        cb = params;
521
        params = null;
522
    }
523
524
    return self.client.post("/address/unspent-outputs", params, {"addresses": addresses}, cb);
525
};
526
527
/**
528
 * verify ownership of an address
529
 *
530
 * @param address       string      address hash
531
 * @param signature     string      a signed message (the address hash) using the private key of the address
532
 * @param [cb]          function    callback function to call when request is complete
533
 * @return q.Promise
534
 */
535
APIClient.prototype.verifyAddress = function(address, signature, cb) {
536
    var self = this;
537
538
    return self.client.post("/address/" + address + "/verify", null, {signature: signature}, cb);
539
};
540
541
/**
542
 * get all blocks (paginated)
543
 *
544
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
545
 * @param [cb]          function    callback function to call when request is complete
546
 * @return q.Promise
547
 */
548
APIClient.prototype.allBlocks = function(params, cb) {
549
    var self = this;
550
551
    if (typeof params === "function") {
552
        cb = params;
553
        params = null;
554
    }
555
556
    return self.client.get("/all-blocks", params, cb);
557
};
558
559
/**
560
 * get a block
561
 *
562
 * @param block         string|int  a block hash or a block height
563
 * @param [cb]          function    callback function to call when request is complete
564
 * @return q.Promise
565
 */
566
APIClient.prototype.block = function(block, cb) {
567
    var self = this;
568
569
    return self.client.get("/block/" + block, null, cb);
570
};
571
572
/**
573
 * get the latest block
574
 *
575
 * @param [cb]          function    callback function to call when request is complete
576
 * @return q.Promise
577
 */
578
APIClient.prototype.blockLatest = function(cb) {
579
    var self = this;
580
581
    return self.client.get("/block/latest", null, cb);
582
};
583
584
/**
585
 * get all transactions for a block (paginated)
586
 *
587
 * @param block         string|int  a block hash or a block height
588
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
589
 * @param [cb]          function    callback function to call when request is complete
590
 * @return q.Promise
591
 */
592
APIClient.prototype.blockTransactions = function(block, params, cb) {
593
    var self = this;
594
595
    if (typeof params === "function") {
596
        cb = params;
597
        params = null;
598
    }
599
600
    return self.client.get("/block/" + block + "/transactions", params, cb);
601
};
602
603
/**
604
 * get a single transaction
605
 *
606
 * @param tx            string      transaction hash
607
 * @param [cb]          function    callback function to call when request is complete
608
 * @return q.Promise
609
 */
610
APIClient.prototype.transaction = function(tx, cb) {
611
    var self = this;
612
613
    return self.client.get("/transaction/" + tx, null, cb);
614
};
615
616
/**
617
 * get a batch of transactions
618
 *
619
 * @param txs           string[]    list of transaction hashes (txId)
620
 * @param [cb]          function    callback function to call when request is complete
621
 * @return q.Promise
622
 */
623
APIClient.prototype.transactions = function(txs, cb) {
624
    var self = this;
625
626
    return self.client.post("/transactions", null, txs, cb, false);
627
};
628
629
/**
630
 * get a paginated list of all webhooks associated with the api user
631
 *
632
 * @param [params]      object      pagination: {page: 1, limit: 20}
633
 * @param [cb]          function    callback function to call when request is complete
634
 * @return q.Promise
635
 */
636
APIClient.prototype.allWebhooks = function(params, cb) {
637
    var self = this;
638
639
    if (typeof params === "function") {
640
        cb = params;
641
        params = null;
642
    }
643
644
    return self.client.get("/webhooks", params, cb);
645
};
646
647
/**
648
 * create a new webhook
649
 *
650
 * @param url           string      the url to receive the webhook events
651
 * @param [identifier]  string      a unique identifier associated with the webhook
652
 * @param [cb]          function    callback function to call when request is complete
653
 * @return q.Promise
654
 */
655
APIClient.prototype.setupWebhook = function(url, identifier, cb) {
656
    var self = this;
657
658
    if (typeof identifier === "function") {
659
        //mimic function overloading
660
        cb = identifier;
661
        identifier = null;
662
    }
663
664
    return self.client.post("/webhook", null, {url: url, identifier: identifier}, cb);
665
};
666
667
/**
668
 * get an existing webhook by it's identifier
669
 *
670
 * @param identifier    string      the unique identifier of the webhook to get
671
 * @param [cb]          function    callback function to call when request is complete
672
 * @return q.Promise
673
 */
674
APIClient.prototype.getWebhook = function(identifier, cb) {
675
    var self = this;
676
677
    return self.client.get("/webhook/" + identifier, null, cb);
678
};
679
680
/**
681
 * update an existing webhook
682
 *
683
 * @param identifier    string      the unique identifier of the webhook
684
 * @param webhookData   object      the data to update: {identifier: newIdentifier, url:newUrl}
685
 * @param [cb]          function    callback function to call when request is complete
686
 * @return q.Promise
687
 */
688
APIClient.prototype.updateWebhook = function(identifier, webhookData, cb) {
689
    var self = this;
690
691
    return self.client.put("/webhook/" + identifier, null, webhookData, cb);
692
};
693
694
/**
695
 * deletes an existing webhook and any event subscriptions associated with it
696
 *
697
 * @param identifier    string      the unique identifier of the webhook
698
 * @param [cb]          function    callback function to call when request is complete
699
 * @return q.Promise
700
 */
701
APIClient.prototype.deleteWebhook = function(identifier, cb) {
702
    var self = this;
703
704
    return self.client.delete("/webhook/" + identifier, null, null, cb);
705
};
706
707
/**
708
 * get a paginated list of all the events a webhook is subscribed to
709
 *
710
 * @param identifier    string      the unique identifier of the webhook
711
 * @param [params]      object      pagination: {page: 1, limit: 20}
712
 * @param [cb]          function    callback function to call when request is complete
713
 * @return q.Promise
714
 */
715
APIClient.prototype.getWebhookEvents = function(identifier, params, cb) {
716
    var self = this;
717
718
    if (typeof params === "function") {
719
        cb = params;
720
        params = null;
721
    }
722
723
    return self.client.get("/webhook/" + identifier + "/events", params, cb);
724
};
725
726
/**
727
 * subscribes a webhook to transaction events for a particular transaction
728
 *
729
 * @param identifier    string      the unique identifier of the webhook
730
 * @param transaction   string      the transaction hash
731
 * @param confirmations integer     the amount of confirmations to send
732
 * @param [cb]          function    callback function to call when request is complete
733
 * @return q.Promise
734
 */
735
APIClient.prototype.subscribeTransaction = function(identifier, transaction, confirmations, cb) {
736
    var self = this;
737
    var postData = {
738
        'event_type': 'transaction',
739
        'transaction': transaction,
740
        'confirmations': confirmations
741
    };
742
743
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
744
};
745
746
/**
747
 * subscribes a webhook to transaction events on a particular address
748
 *
749
 * @param identifier    string      the unique identifier of the webhook
750
 * @param address       string      the address hash
751
 * @param confirmations integer     the amount of confirmations to send
752
 * @param [cb]          function    callback function to call when request is complete
753
 * @return q.Promise
754
 */
755
APIClient.prototype.subscribeAddressTransactions = function(identifier, address, confirmations, cb) {
756
    var self = this;
757
    var postData = {
758
        'event_type': 'address-transactions',
759
        'address': address,
760
        'confirmations': confirmations
761
    };
762
763
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
764
};
765
766
/**
767
 * batch subscribes a webhook to multiple transaction events
768
 *
769
 * @param  identifier   string      the unique identifier of the webhook
770
 * @param  batchData    array       An array of objects containing batch event data:
771
 *                                  {address : 'address', confirmations : 'confirmations']
772
 *                                  where address is the address to subscribe to and confirmations (optional) is the amount of confirmations to send
773
 * @param [cb]          function    callback function to call when request is complete
774
 * @return q.Promise
775
 */
776
APIClient.prototype.batchSubscribeAddressTransactions = function(identifier, batchData, cb) {
777
    var self = this;
778
    batchData.forEach(function(record) {
779
        record.event_type = 'address-transactions';
780
    });
781
782
    return self.client.post("/webhook/" + identifier + "/events/batch", null, batchData, cb);
783
};
784
785
/**
786
 * subscribes a webhook to a new block event
787
 *
788
 * @param identifier    string      the unique identifier of the webhook
789
 * @param [cb]          function    callback function to call when request is complete
790
 * @return q.Promise
791
 */
792
APIClient.prototype.subscribeNewBlocks = function(identifier, cb) {
793
    var self = this;
794
    var postData = {
795
        'event_type': 'block'
796
    };
797
798
    return self.client.post("/webhook/" + identifier + "/events", null, postData, cb);
799
};
800
801
/**
802
 * removes an address transaction event subscription from a webhook
803
 *
804
 * @param identifier    string      the unique identifier of the webhook
805
 * @param address       string      the address hash
806
 * @param [cb]          function    callback function to call when request is complete
807
 * @return q.Promise
808
 */
809
APIClient.prototype.unsubscribeAddressTransactions = function(identifier, address, cb) {
810
    var self = this;
811
812
    return self.client.delete("/webhook/" + identifier + "/address-transactions/" + address, null, null, cb);
813
};
814
815
/**
816
 * removes an transaction event subscription from a webhook
817
 *
818
 * @param identifier    string      the unique identifier of the webhook
819
 * @param transaction   string      the transaction hash
820
 * @param [cb]          function    callback function to call when request is complete
821
 * @return q.Promise
822
 */
823
APIClient.prototype.unsubscribeTransaction = function(identifier, transaction, cb) {
824
    var self = this;
825
826
    return self.client.delete("/webhook/" + identifier + "/transaction/" + transaction, null, null, cb);
827
};
828
829
/**
830
 * removes a block event subscription from a webhook
831
 *
832
 * @param identifier    string      the unique identifier of the webhook
833
 * @param [cb]          function    callback function to call when request is complete
834
 * @return q.Promise
835
 */
836
APIClient.prototype.unsubscribeNewBlocks = function(identifier, cb) {
837
    var self = this;
838
839
    return self.client.delete("/webhook/" + identifier + "/block", null, null, cb);
840
};
841
842
/**
843
 * initialize an existing wallet
844
 *
845
 * Either takes two argument:
846
 * @param options       object      {}
847
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
848
 *
849
 * Or takes three arguments (old, deprecated syntax):
850
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
851
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
852
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 847. The second definition is ignored.
Loading history...
853
 *
854
 * @returns {q.Promise}
855
 */
856
APIClient.prototype.initWallet = function(options, cb) {
857
    var self = this;
858
859
    if (typeof options !== "object") {
860
        // get the old-style arguments
861
        options = {
862
            identifier: arguments[0],
863
            passphrase: arguments[1]
864
        };
865
866
        cb = arguments[2];
867
    }
868
869
    var deferred = q.defer();
870
    deferred.promise.spreadNodeify(cb);
871
872
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
873
874
    var identifier = options.identifier;
875
876
    if (!identifier) {
877
        deferred.reject(new blocktrail.WalletInitError("Identifier is required"));
878
        return deferred.promise;
879
    }
880
881
    deferred.resolve(self.client.get("/wallet/" + identifier, null, true).then(function(result) {
882
        var keyIndex = options.keyIndex || result.key_index;
883
884
        options.walletVersion = result.wallet_version;
885
886
        var backupPublicKey = bitcoin.HDNode.fromBase58(result.backup_public_key[0], network);
887
        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
888
            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
889
        });
890
        var primaryPublicKeys = _.mapValues(result.primary_public_keys, function(primaryPublicKey) {
891
            return bitcoin.HDNode.fromBase58(primaryPublicKey[0], self.network);
892
        });
893
894
        // initialize wallet
895
        var wallet = new Wallet(
896
            self,
897
            identifier,
898
            options.walletVersion,
899
            result.primary_mnemonic,
900
            result.encrypted_primary_seed,
901
            result.encrypted_secret,
902
            primaryPublicKeys,
903
            backupPublicKey,
904
            blocktrailPublicKeys,
905
            keyIndex,
906
            result.chain || 0,
907
            result.segwit || 0,
908
            self.testnet,
909
            result.checksum,
910
            result.upgrade_key_index,
911
            options.bypassNewAddressCheck
912
        );
913
914
        wallet.recoverySecret = result.recovery_secret;
915
916
        if (!options.readOnly) {
917
            return wallet.unlock(options).then(function() {
918
                return wallet;
919
            });
920
        } else {
921
            return wallet;
922
        }
923
    }));
924
925
    return deferred.promise;
926
};
927
928
APIClient.CREATE_WALLET_PROGRESS_START = 0;
929
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_SECRET = 4;
930
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_PRIMARY = 5;
931
APIClient.CREATE_WALLET_PROGRESS_ENCRYPT_RECOVERY = 6;
932
APIClient.CREATE_WALLET_PROGRESS_PRIMARY = 10;
933
APIClient.CREATE_WALLET_PROGRESS_BACKUP = 20;
934
APIClient.CREATE_WALLET_PROGRESS_SUBMIT = 30;
935
APIClient.CREATE_WALLET_PROGRESS_INIT = 40;
936
APIClient.CREATE_WALLET_PROGRESS_DONE = 100;
937
938
/**
939
 * create a new wallet
940
 *   - will generate a new primary seed and backup seed
941
 *
942
 * Either takes two argument:
943
 * @param options       object      {}
944
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys) // nocommit @TODO
945
 *
946
 * For v1 wallets (explicitly specify options.walletVersion=v1):
947
 * @param options       object      {}
0 ignored issues
show
Documentation introduced by
The parameter options has already been documented on line 943. The second definition is ignored.
Loading history...
948
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 944. The second definition is ignored.
Loading history...
949
 *
950
 * Or takes four arguments (old, deprecated syntax):
951
 * @param identifier    string      the wallet identifier to be initialized
0 ignored issues
show
Documentation introduced by
The parameter identifier does not exist. Did you maybe forget to remove this comment?
Loading history...
952
 * @param passphrase    string      the password to decrypt the mnemonic with
0 ignored issues
show
Documentation introduced by
The parameter passphrase does not exist. Did you maybe forget to remove this comment?
Loading history...
953
 * @param keyIndex      int         override for the blocktrail cosign key to use (for development purposes)
0 ignored issues
show
Documentation introduced by
The parameter keyIndex does not exist. Did you maybe forget to remove this comment?
Loading history...
954
 * @param [cb]          function    callback(err, wallet, primaryMnemonic, backupMnemonic, blocktrailPubKeys)
0 ignored issues
show
Documentation introduced by
The parameter cb has already been documented on line 944. The second definition is ignored.
Loading history...
955
 * @returns {q.Promise}
956
 */
957
APIClient.prototype.createNewWallet = function(options, cb) {
958
    /* jshint -W071, -W074 */
959
960
    var self = this;
961
962
    if (typeof options !== "object") {
963
        // get the old-style arguments
964
        var identifier = arguments[0];
965
        var passphrase = arguments[1];
966
        var keyIndex = arguments[2];
967
        cb = arguments[3];
968
969
        // keyIndex is optional
970
        if (typeof keyIndex === "function") {
971
            cb = keyIndex;
972
            keyIndex = null;
973
        }
974
975
        options = {
976
            identifier: identifier,
977
            passphrase: passphrase,
978
            keyIndex: keyIndex
979
        };
980
    }
981
982
    // default to v3
983
    options.walletVersion = options.walletVersion || Wallet.WALLET_VERSION_V3;
984
985
    var deferred = q.defer();
986
    deferred.promise.spreadNodeify(cb);
987
988
    q.nextTick(function() {
989
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_START);
990
991
        options.keyIndex = options.keyIndex || 0;
992
        options.passphrase = options.passphrase || options.password;
993
        delete options.password;
994
995
        if (!options.identifier) {
996
            deferred.reject(new blocktrail.WalletCreateError("Identifier is required"));
997
            return deferred.promise;
998
        }
999
1000
        if (options.walletVersion === Wallet.WALLET_VERSION_V1) {
1001
            self._createNewWalletV1(options)
1002
                .progress(function(p) { deferred.notify(p); })
1003
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1004
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1005
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V2) {
1006
            self._createNewWalletV2(options)
1007
                .progress(function(p) { deferred.notify(p); })
1008
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1009
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1010
        } else if (options.walletVersion === Wallet.WALLET_VERSION_V3) {
1011
            self._createNewWalletV3(options)
1012
                .progress(function(p) { deferred.notify(p); })
1013
                .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); })
1014
            ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1015
        } else {
1016
            deferred.reject(new blocktrail.WalletCreateError("Invalid wallet version!"));
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1017
        }
1018
    });
1019
1020
    return deferred.promise;
1021
};
1022
1023
APIClient.prototype._createNewWalletV1 = function(options) {
1024
    var self = this;
1025
1026
    var deferred = q.defer();
1027
1028
    q.nextTick(function() {
1029
1030
        if (!options.primaryMnemonic && !options.primarySeed) {
1031
            if (!options.passphrase && !options.password) {
1032
                deferred.reject(new blocktrail.WalletCreateError("Can't generate Primary Mnemonic without a passphrase"));
1033
                return deferred.promise;
1034
            } else {
1035
                options.primaryMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1036
                if (options.storePrimaryMnemonic !== false) {
1037
                    options.storePrimaryMnemonic = true;
1038
                }
1039
            }
1040
        }
1041
1042
        if (!options.backupMnemonic && !options.backupPublicKey) {
1043
            options.backupMnemonic = bip39.generateMnemonic(Wallet.WALLET_ENTROPY_BITS);
1044
        }
1045
1046
        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_PRIMARY);
1047
1048
        self.resolvePrimaryPrivateKeyFromOptions(options)
1049
            .then(function(options) {
1050
                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_BACKUP);
1051
1052
                return self.resolveBackupPublicKeyFromOptions(options)
1053
                    .then(function(options) {
1054
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_SUBMIT);
1055
1056
                        // create a checksum of our private key which we'll later use to verify we used the right password
1057
                        var checksum = options.primaryPrivateKey.getAddress();
1058
                        var keyIndex = options.keyIndex;
1059
1060
                        var primaryPublicKey = options.primaryPrivateKey.deriveHardened(keyIndex).neutered();
1061
1062
                        // send the public keys to the server to store them
1063
                        //  and the mnemonic, which is safe because it's useless without the password
1064
                        return self.storeNewWalletV1(
1065
                            options.identifier,
1066
                            [primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1067
                            [options.backupPublicKey.toBase58(), "M"],
1068
                            options.storePrimaryMnemonic ? options.primaryMnemonic : false,
1069
                            checksum,
1070
                            keyIndex
1071
                        )
1072
                            .then(function(result) {
1073
                                deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1074
1075
                                var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1076
                                    return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1077
                                });
1078
1079
                                var wallet = new Wallet(
1080
                                    self,
1081
                                    options.identifier,
1082
                                    Wallet.WALLET_VERSION_V1,
1083
                                    options.primaryMnemonic,
1084
                                    null,
1085
                                    null,
1086
                                    {keyIndex: primaryPublicKey},
1087
                                    options.backupPublicKey,
1088
                                    blocktrailPublicKeys,
1089
                                    keyIndex,
1090
                                    result.chain || 0,
1091
                                    result.segwit || 0,
1092
                                    self.testnet,
1093
                                    checksum,
1094
                                    result.upgrade_key_index,
1095
                                    options.bypassNewAddressCheck
1096
                                );
1097
1098
                                return wallet.unlock({
1099
                                    walletVersion: Wallet.WALLET_VERSION_V1,
1100
                                    passphrase: options.passphrase,
1101
                                    primarySeed: options.primarySeed,
1102
                                    primaryMnemonic: null // explicit null
1103
                                }).then(function() {
1104
                                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1105
                                    return [
1106
                                        wallet,
1107
                                        {
1108
                                            walletVersion: wallet.walletVersion,
1109
                                            primaryMnemonic: options.primaryMnemonic,
1110
                                            backupMnemonic: options.backupMnemonic,
1111
                                            blocktrailPublicKeys: blocktrailPublicKeys
1112
                                        }
1113
                                    ];
1114
                                });
1115
                            });
1116
                    }
1117
                );
1118
            })
1119
            .then(
1120
            function(r) {
1121
                deferred.resolve(r);
1122
            },
1123
            function(e) {
1124
                deferred.reject(e);
1125
            }
1126
        )
1127
        ;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1128
    });
1129
1130
    return deferred.promise;
1131
};
1132
1133
APIClient.prototype._createNewWalletV2 = function(options) {
1134
    var self = this;
1135
1136
    var deferred = q.defer();
1137
1138
    // avoid modifying passed options
1139
    options = _.merge({}, options);
1140 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1141
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
1142
1143
    determineDataStorageV2_3(options)
1144
        .then(function(options) {
1145
            options.passphrase = options.passphrase || options.password;
1146
            delete options.password;
1147
1148
            // avoid deprecated options
1149
            if (options.primaryPrivateKey) {
1150
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1151
            }
1152
1153
            // seed should be provided or generated
1154
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1155
1156
            return options;
1157
        })
1158
        .then(function(options) {
1159
            return produceEncryptedDataV2(options, deferred.notify.bind(deferred));
1160
        })
1161
        .then(function(options) {
1162
            return doRemainingWalletDataV2_3(options, network, deferred.notify.bind(deferred));
1163
        })
1164
        .then(function(options) {
1165
            // create a checksum of our private key which we'll later use to verify we used the right password
1166
            var checksum = options.primaryPrivateKey.getAddress();
1167
            var keyIndex = options.keyIndex;
1168
1169
            // send the public keys and encrypted data to server
1170
            return self.storeNewWalletV2(
1171
                options.identifier,
1172
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1173
                [options.backupPublicKey.toBase58(), "M"],
1174
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1175
                options.storeDataOnServer ? options.encryptedSecret : false,
1176
                options.storeDataOnServer ? options.recoverySecret : false,
1177
                checksum,
1178
                keyIndex,
1179
                options.support_secret || null
1180
            )
1181
                .then(
1182
                function(result) {
1183
                    deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1184
1185
                    var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1186
                        return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1187
                    });
1188
1189
                    var wallet = new Wallet(
1190
                        self,
1191
                        options.identifier,
1192
                        Wallet.WALLET_VERSION_V2,
1193
                        null,
1194
                        options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1195
                        options.storeDataOnServer ? options.encryptedSecret : null,
1196
                        {keyIndex: options.primaryPublicKey},
1197
                        options.backupPublicKey,
1198
                        blocktrailPublicKeys,
1199
                        keyIndex,
1200
                        result.chain || 0,
1201
                        result.segwit || 0,
1202
                        self.testnet,
1203
                        checksum,
1204
                        result.upgrade_key_index,
1205
                        options.bypassNewAddressCheck
1206
                    );
1207
1208
                    // pass along decrypted data to avoid extra work
1209
                    return wallet.unlock({
1210
                        walletVersion: Wallet.WALLET_VERSION_V2,
1211
                        passphrase: options.passphrase,
1212
                        primarySeed: options.primarySeed,
1213
                        secret: options.secret
1214
                    }).then(function() {
1215
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1216
                        return [
1217
                            wallet,
1218
                            {
1219
                                walletVersion: wallet.walletVersion,
1220
                                encryptedPrimarySeed: options.encryptedPrimarySeed ?
1221
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedPrimarySeed, 'base64', 'hex')) :
1222
                                    null,
1223
                                backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed.toString('hex')) : null,
1224
                                recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1225
                                    bip39.entropyToMnemonic(blocktrail.convert(options.recoveryEncryptedSecret, 'base64', 'hex')) :
1226
                                    null,
1227
                                encryptedSecret: options.encryptedSecret ?
1228
                                    bip39.entropyToMnemonic(blocktrail.convert(options.encryptedSecret, 'base64', 'hex')) :
1229
                                    null,
1230
                                blocktrailPublicKeys: blocktrailPublicKeys
1231
                            }
1232
                        ];
1233
                    });
1234
                }
1235
            );
1236
        })
1237
       .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1238
1239
    return deferred.promise;
1240
};
1241
1242
APIClient.prototype._createNewWalletV3 = function(options) {
1243
    var self = this;
1244
1245
    var deferred = q.defer();
1246
1247
    // avoid modifying passed options
1248
    options = _.merge({}, options);
1249 View Code Duplication
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1250
    var network = self.testnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;
1251
1252
    determineDataStorageV2_3(options)
1253
        .then(function(options) {
1254
            options.passphrase = options.passphrase || options.password;
1255
            delete options.password;
1256
1257
            // avoid deprecated options
1258
            if (options.primaryPrivateKey) {
1259
                throw new blocktrail.WalletInitError("Can't specify; Primary PrivateKey");
1260
            }
1261
1262
            // seed should be provided or generated
1263
            options.primarySeed = options.primarySeed || randomBytes(Wallet.WALLET_ENTROPY_BITS / 8);
1264
1265
            return options;
1266
        })
1267
        .then(function(options) {
1268
            return self.produceEncryptedDataV3(options, deferred.notify.bind(deferred));
1269
        })
1270
        .then(function(options) {
1271
            return doRemainingWalletDataV2_3(options, network, deferred.notify.bind(deferred));
1272
        })
1273
        .then(function(options) {
1274
1275
            // create a checksum of our private key which we'll later use to verify we used the right password
1276
            var checksum = options.primaryPrivateKey.getAddress();
1277
            var keyIndex = options.keyIndex;
1278
1279
            // send the public keys and encrypted data to server
1280
            return self.storeNewWalletV3(
1281
                options.identifier,
1282
                [options.primaryPublicKey.toBase58(), "M/" + keyIndex + "'"],
1283
                [options.backupPublicKey.toBase58(), "M"],
1284
                options.storeDataOnServer ? options.encryptedPrimarySeed : false,
1285
                options.storeDataOnServer ? options.encryptedSecret : false,
1286
                options.storeDataOnServer ? options.recoverySecret : false,
1287
                checksum,
1288
                keyIndex,
1289
                options.support_secret || null
1290
            )
1291
                .then(
1292
                    // result, deferred, self(apiclient)
1293
                    function(result) {
1294
                        deferred.notify(APIClient.CREATE_WALLET_PROGRESS_INIT);
1295
1296
                        var blocktrailPublicKeys = _.mapValues(result.blocktrail_public_keys, function(blocktrailPublicKey) {
1297
                            return bitcoin.HDNode.fromBase58(blocktrailPublicKey[0], self.network);
1298
                        });
1299
1300
                        var wallet = new Wallet(
1301
                            self,
1302
                            options.identifier,
1303
                            Wallet.WALLET_VERSION_V3,
1304
                            null,
1305
                            options.storeDataOnServer ? options.encryptedPrimarySeed : null,
1306
                            options.storeDataOnServer ? options.encryptedSecret : null,
1307
                            {keyIndex: options.primaryPublicKey},
1308
                            options.backupPublicKey,
1309
                            blocktrailPublicKeys,
1310
                            keyIndex,
1311
                            result.chain || 0,
1312
                            result.segwit || 0,
1313
                            self.testnet,
1314
                            checksum,
1315
                            result.upgrade_key_index,
1316
                            options.bypassNewAddressCheck
1317
                        );
1318
1319
                        // pass along decrypted data to avoid extra work
1320
                        return wallet.unlock({
1321
                            walletVersion: Wallet.WALLET_VERSION_V3,
1322
                            passphrase: options.passphrase,
1323
                            primarySeed: options.primarySeed,
1324
                            secret: options.secret
1325
                        }).then(function() {
1326
                            deferred.notify(APIClient.CREATE_WALLET_PROGRESS_DONE);
1327
                            return [
1328
                                wallet,
1329
                                {
1330
                                    walletVersion: wallet.walletVersion,
1331
                                    encryptedPrimarySeed: options.encryptedPrimarySeed ? EncryptionMnemonic.encode(options.encryptedPrimarySeed) : null,
1332
                                    backupSeed: options.backupSeed ? bip39.entropyToMnemonic(options.backupSeed) : null,
1333
                                    recoveryEncryptedSecret: options.recoveryEncryptedSecret ?
1334
                                        EncryptionMnemonic.encode(options.recoveryEncryptedSecret) : null,
1335
                                    encryptedSecret: options.encryptedSecret ? EncryptionMnemonic.encode(options.encryptedSecret) : null,
1336
                                    blocktrailPublicKeys: blocktrailPublicKeys
1337
                                }
1338
                            ];
1339
                        });
1340
                    }
1341
                );
1342
        })
1343
        .then(function(r) { deferred.resolve(r); }, function(e) { deferred.reject(e); });
1344
1345
    return deferred.promise;
1346
};
1347
1348
function verifyPublicBip32Key(bip32Key, network) {
1349
    var hk = bitcoin.HDNode.fromBase58(bip32Key[0], network);
1350
    if (typeof hk.keyPair.d !== "undefined") {
1351
        throw new Error('BIP32Key contained private key material - abort');
1352
    }
1353
1354
    if (bip32Key[1].slice(0, 1) !== "M") {
1355
        throw new Error("BIP32Key contained non-public path - abort");
1356
    }
1357
}
1358
1359
function verifyPublicOnly(walletData, network) {
1360
    verifyPublicBip32Key(walletData.primary_public_key, network);
1361
    verifyPublicBip32Key(walletData.backup_public_key, network);
1362
}
1363
1364
/**
1365
 * create wallet using the API
1366
 *
1367
 * @param identifier            string      the wallet identifier to create
1368
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1369
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1370
 * @param primaryMnemonic       string      mnemonic to store
1371
 * @param checksum              string      checksum to store
1372
 * @param keyIndex              int         keyIndex that was used to create wallet
1373
 * @param [cb]                  function    callback(err, result)
1374
 * @returns {q.Promise}
1375
 */
1376
APIClient.prototype.storeNewWalletV1 = function(identifier, primaryPublicKey, backupPublicKey, primaryMnemonic, checksum, keyIndex, cb) {
1377
    var self = this;
1378
1379
    var postData = {
1380
        identifier: identifier,
1381
        wallet_version: Wallet.WALLET_VERSION_V1,
1382
        primary_public_key: primaryPublicKey,
1383
        backup_public_key: backupPublicKey,
1384
        primary_mnemonic: primaryMnemonic,
1385
        checksum: checksum,
1386
        key_index: keyIndex
1387
    };
1388
1389
    verifyPublicOnly(postData, self.network);
1390
1391
    return self.client.post("/wallet", null, postData, cb);
1392
};
1393
1394
/**
1395
 * create wallet using the API
1396
 *
1397
 * @param identifier            string      the wallet identifier to create
1398
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1399
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1400
 * @param encryptedPrimarySeed  string      openssl format
1401
 * @param encryptedSecret       string      openssl format
1402
 * @param recoverySecret        string      openssl format
1403
 * @param checksum              string      checksum to store
1404
 * @param keyIndex              int         keyIndex that was used to create wallet
1405
 * @param supportSecret         string
1406
 * @param [cb]                  function    callback(err, result)
1407
 * @returns {q.Promise}
1408
 */
1409
APIClient.prototype.storeNewWalletV2 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1410
                                                recoverySecret, checksum, keyIndex, supportSecret, cb) {
1411
    var self = this;
1412
1413
    var postData = {
1414
        identifier: identifier,
1415
        wallet_version: Wallet.WALLET_VERSION_V2,
1416
        primary_public_key: primaryPublicKey,
1417
        backup_public_key: backupPublicKey,
1418
        encrypted_primary_seed: encryptedPrimarySeed,
1419
        encrypted_secret: encryptedSecret,
1420
        recovery_secret: recoverySecret,
1421
        checksum: checksum,
1422
        key_index: keyIndex,
1423
        support_secret: supportSecret || null
1424
    };
1425
1426
    verifyPublicOnly(postData, self.network);
1427
1428
    return self.client.post("/wallet", null, postData, cb);
1429
};
1430
1431
/**
1432
 * create wallet using the API
1433
 *
1434
 * @param identifier            string      the wallet identifier to create
1435
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1436
 * @param backupPublicKey       array       the backup public key - [key, path] should be M/<keyIndex>'
1437
 * @param encryptedPrimarySeed  Buffer      buffer of ciphertext
1438
 * @param encryptedSecret       Buffer      buffer of ciphertext
1439
 * @param recoverySecret        Buffer      buffer of recovery secret
1440
 * @param checksum              string      checksum to store
1441
 * @param keyIndex              int         keyIndex that was used to create wallet
1442
 * @param supportSecret         string
1443
 * @param [cb]                  function    callback(err, result)
1444
 * @returns {q.Promise}
1445
 */
1446
APIClient.prototype.storeNewWalletV3 = function(identifier, primaryPublicKey, backupPublicKey, encryptedPrimarySeed, encryptedSecret,
1447
                                                recoverySecret, checksum, keyIndex, supportSecret, cb) {
1448
    var self = this;
1449
1450
    var postData = {
1451
        identifier: identifier,
1452
        wallet_version: Wallet.WALLET_VERSION_V3,
1453
        primary_public_key: primaryPublicKey,
1454
        backup_public_key: backupPublicKey,
1455
        encrypted_primary_seed: encryptedPrimarySeed.toString('base64'),
1456
        encrypted_secret: encryptedSecret.toString('base64'),
1457
        recovery_secret: recoverySecret.toString('hex'),
1458
        checksum: checksum,
1459
        key_index: keyIndex,
1460
        support_secret: supportSecret || null
1461
    };
1462
1463
    verifyPublicOnly(postData, self.network);
1464
1465
    return self.client.post("/wallet", null, postData, cb);
1466
};
1467
1468
/**
1469
 * create wallet using the API
1470
 *
1471
 * @param identifier            string      the wallet identifier to create
1472
 * @param postData              object
1473
 * @param [cb]                  function    callback(err, result)
1474
 * @returns {q.Promise}
1475
 */
1476
APIClient.prototype.updateWallet = function(identifier, postData, cb) {
1477
    var self = this;
1478
1479
    return self.client.post("/wallet/" + identifier, null, postData, cb);
1480
};
1481
1482
/**
1483
 * upgrade wallet to use a new account number
1484
 *  the account number specifies which blocktrail cosigning key is used
1485
 *
1486
 * @param identifier            string      the wallet identifier
1487
 * @param primaryPublicKey      array       the primary public key - [key, path] should be M/<keyIndex>'
1488
 * @param keyIndex              int         keyIndex that was used to create wallet
1489
 * @param [cb]                  function    callback(err, result)
1490
 * @returns {q.Promise}
1491
 */
1492
APIClient.prototype.upgradeKeyIndex = function(identifier, keyIndex, primaryPublicKey, cb) {
1493
    var self = this;
1494
1495
    return self.client.post("/wallet/" + identifier + "/upgrade", null, {
1496
        key_index: keyIndex,
1497
        primary_public_key: primaryPublicKey
1498
    }, cb);
1499
};
1500
1501
/**
1502
 * get the balance for the wallet
1503
 *
1504
 * @param identifier            string      the wallet identifier
1505
 * @param [cb]                  function    callback(err, result)
1506
 * @returns {q.Promise}
1507
 */
1508
APIClient.prototype.getWalletBalance = function(identifier, cb) {
1509
    var self = this;
1510
1511
    return self.client.get("/wallet/" + identifier + "/balance", null, true, cb);
1512
};
1513
1514
/**
1515
 * do HD wallet discovery for the wallet
1516
 *
1517
 * @param identifier            string      the wallet identifier
1518
 * @param [cb]                  function    callback(err, result)
1519
 * @returns {q.Promise}
1520
 */
1521
APIClient.prototype.doWalletDiscovery = function(identifier, gap, cb) {
1522
    var self = this;
1523
1524
    return self.client.get("/wallet/" + identifier + "/discovery", {gap: gap}, true, cb);
1525
};
1526
1527
1528
/**
1529
 * get a new derivation number for specified parent path
1530
 *  eg; m/44'/1'/0/0 results in m/44'/1'/0/0/0 and next time in m/44'/1'/0/0/1 and next time in m/44'/1'/0/0/2
1531
 *
1532
 * @param identifier            string      the wallet identifier
1533
 * @param path                  string      the parent path for which to get a new derivation,
1534
 *                                           can be suffixed with /* to make it clear on which level the derivations hould be
1535
 * @param [cb]                  function    callback(err, result)
1536
 * @returns {q.Promise}
1537
 */
1538
APIClient.prototype.getNewDerivation = function(identifier, path, cb) {
1539
    var self = this;
1540
1541
    return self.client.post("/wallet/" + identifier + "/path", null, {path: path}, cb);
1542
};
1543
1544
1545
/**
1546
 * delete the wallet
1547
 *  the checksum address and a signature to verify you ownership of the key of that checksum address
1548
 *  is required to be able to delete a wallet
1549
 *
1550
 * @param identifier            string      the wallet identifier
1551
 * @param checksumAddress       string      the address for your master private key (and the checksum used when creating the wallet)
1552
 * @param checksumSignature     string      a signature of the checksum address as message signed by the private key matching that address
1553
 * @param [force]               bool        ignore warnings (such as a non-zero balance)
1554
 * @param [cb]                  function    callback(err, result)
1555
 * @returns {q.Promise}
1556
 */
1557
APIClient.prototype.deleteWallet = function(identifier, checksumAddress, checksumSignature, force, cb) {
1558
    var self = this;
1559
1560
    if (typeof force === "function") {
1561
        cb = force;
1562
        force = false;
1563
    }
1564
1565
    return self.client.delete("/wallet/" + identifier, {force: force}, {
1566
        checksum: checksumAddress,
1567
        signature: checksumSignature
1568
    }, cb);
1569
};
1570
1571
/**
1572
 * use the API to get the best inputs to use based on the outputs
1573
 *
1574
 * the return array has the following format:
1575
 * [
1576
 *  "utxos" => [
1577
 *      [
1578
 *          "hash" => "<txHash>",
1579
 *          "idx" => "<index of the output of that <txHash>",
1580
 *          "scriptpubkey_hex" => "<scriptPubKey-hex>",
1581
 *          "value" => 32746327,
1582
 *          "address" => "1address",
1583
 *          "path" => "m/44'/1'/0'/0/13",
1584
 *          "redeem_script" => "<redeemScript-hex>",
1585
 *      ],
1586
 *  ],
1587
 *  "fee"   => 10000,
1588
 *  "change"=> 1010109201,
1589
 * ]
1590
 *
1591
 * @param identifier        string      the wallet identifier
1592
 * @param pay               array       {'address': (int)value}     coins to send
1593
 * @param lockUTXO          bool        lock UTXOs for a few seconds to allow for transaction to be created
1594
 * @param allowZeroConf     bool        allow zero confirmation unspent outputs to be used in coin selection
1595
 * @param feeStrategy       string      defaults to
1596
 * @param options
1597
 * @param [cb]              function    callback(err, utxos, fee, change)
1598
 * @returns {q.Promise}
1599
 */
1600
APIClient.prototype.coinSelection = function(identifier, pay, lockUTXO, allowZeroConf, feeStrategy, options, cb) {
1601
    var self = this;
1602
1603
    if (typeof feeStrategy === "function") {
1604
        cb = feeStrategy;
1605
        feeStrategy = null;
1606
        options = {};
1607
    } else if (typeof options === "function") {
1608
        cb = options;
1609
        options = {};
1610
    }
1611
1612
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1613
    options = options || {};
1614
1615
    var deferred = q.defer();
1616
    deferred.promise.spreadNodeify(cb);
1617
1618
    var params = {
1619
        lock: lockUTXO,
1620
        zeroconf: allowZeroConf ? 1 : 0,
1621
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1622
        fee_strategy: feeStrategy
1623
    };
1624
1625
    if (options.forcefee) {
1626
        params['forcefee'] = options.forcefee;
1627
    }
1628
1629
    deferred.resolve(
1630
        self.client.post("/wallet/" + identifier + "/coin-selection", params, pay).then(
1631
            function(result) {
1632
                return [result.utxos, result.fee, result.change, result];
1633
            },
1634
            function(err) {
1635
                if (err.message.match(/too low to pay the fee/)) {
1636
                    throw blocktrail.WalletFeeError(err);
1637
                }
1638
1639
                throw err;
1640
            }
1641
        )
1642
    );
1643
1644
    return deferred.promise;
1645
};
1646
1647
/**
1648
 * @param [cb]              function    callback(err, utxos, fee, change)
1649
 * @returns {q.Promise}
1650
 */
1651
APIClient.prototype.feePerKB = function(cb) {
1652
    var self = this;
1653
1654
    var deferred = q.defer();
1655
    deferred.promise.spreadNodeify(cb);
1656
1657
    deferred.resolve(self.client.get("/fee-per-kb"));
1658
1659
    return deferred.promise;
1660
};
1661
1662
/**
1663
 * send the transaction using the API
1664
 *
1665
 * @param identifier        string      the wallet identifier
1666
 * @param txHex             string      partially signed transaction as hex string
1667
 * @param paths             array       list of paths used in inputs which should be cosigned by the API
1668
 * @param checkFee          bool        when TRUE the API will verify if the fee is 100% correct and otherwise throw an exception
1669
 * @param [twoFactorToken]  string      2FA token
1670
 * @param [prioboost]       bool
1671
 * @param [cb]              function    callback(err, txHash)
1672
 * @returns {q.Promise}
1673
 */
1674
APIClient.prototype.sendTransaction = function(identifier, txHex, paths, checkFee, twoFactorToken, prioboost, cb) {
1675
    var self = this;
1676
1677
    if (typeof twoFactorToken === "function") {
1678
        cb = twoFactorToken;
1679
        twoFactorToken = null;
1680
        prioboost = false;
1681
    } else if (typeof prioboost === "function") {
1682
        cb = prioboost;
1683
        prioboost = false;
1684
    }
1685
1686
    var data = {
1687
        paths: paths,
1688
        two_factor_token: twoFactorToken
1689
    };
1690
    if (typeof txHex === "string") {
1691
        data.raw_transaction = txHex;
1692
    } else if (typeof txHex === "object") {
1693
        Object.keys(txHex).map(function(key) {
1694
            data[key] = txHex[key];
1695
        });
1696
    }
1697
1698
    return self.client.post(
1699
        "/wallet/" + identifier + "/send",
1700
        {
1701
            check_fee: checkFee ? 1 : 0,
1702
            prioboost: prioboost ? 1 : 0
1703
        },
1704
        data,
1705
        cb
1706
    );
1707
};
1708
1709
/**
1710
 * setup a webhook for this wallet
1711
 *
1712
 * @param identifier        string      the wallet identifier
1713
 * @param webhookIdentifier string      identifier for the webhook
1714
 * @param url               string      URL to receive webhook events
1715
 * @param [cb]              function    callback(err, webhook)
1716
 * @returns {q.Promise}
1717
 */
1718
APIClient.prototype.setupWalletWebhook = function(identifier, webhookIdentifier, url, cb) {
1719
    var self = this;
1720
1721
    return self.client.post("/wallet/" + identifier + "/webhook", null, {url: url, identifier: webhookIdentifier}, cb);
1722
};
1723
1724
/**
1725
 * delete a webhook that was created for this wallet
1726
 *
1727
 * @param identifier        string      the wallet identifier
1728
 * @param webhookIdentifier string      identifier for the webhook
1729
 * @param [cb]              function    callback(err, success)
1730
 * @returns {q.Promise}
1731
 */
1732
APIClient.prototype.deleteWalletWebhook = function(identifier, webhookIdentifier, cb) {
1733
    var self = this;
1734
1735
    return self.client.delete("/wallet/" + identifier + "/webhook/" + webhookIdentifier, null, null, cb);
1736
};
1737
1738
/**
1739
 * get all transactions for an wallet (paginated)
1740
 *
1741
 * @param identifier    string      wallet identifier
1742
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1743
 * @param [cb]          function    callback function to call when request is complete
1744
 * @return q.Promise
1745
 */
1746
APIClient.prototype.walletTransactions = function(identifier, params, cb) {
1747
    var self = this;
1748
1749
    if (typeof params === "function") {
1750
        cb = params;
1751
        params = null;
1752
    }
1753
1754
    return self.client.get("/wallet/" + identifier + "/transactions", params, true, cb);
1755
};
1756
1757
/**
1758
 * get all addresses for an wallet (paginated)
1759
 *
1760
 * @param identifier    string      wallet identifier
1761
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1762
 * @param [cb]          function    callback function to call when request is complete
1763
 * @return q.Promise
1764
 */
1765
APIClient.prototype.walletAddresses = function(identifier, params, cb) {
1766
    var self = this;
1767
1768
    if (typeof params === "function") {
1769
        cb = params;
1770
        params = null;
1771
    }
1772
1773
    return self.client.get("/wallet/" + identifier + "/addresses", params, true, cb);
1774
};
1775
1776
/**
1777
 * @param identifier    string      wallet identifier
1778
 * @param address       string      the address to label
1779
 * @param label         string      the label
1780
 * @param [cb]          function    callback(err, res)
1781
 * @return q.Promise
1782
 */
1783
APIClient.prototype.labelWalletAddress = function(identifier, address, label, cb) {
1784
    var self = this;
1785
1786
    return self.client.post("/wallet/" + identifier + "/address/" + address + "/label", null, {label: label}, cb);
1787
};
1788
1789
APIClient.prototype.walletMaxSpendable = function(identifier, allowZeroConf, feeStrategy, options, cb) {
1790
    var self = this;
1791
1792
    if (typeof feeStrategy === "function") {
1793
        cb = feeStrategy;
1794
        feeStrategy = null;
1795
    } else if (typeof options === "function") {
1796
        cb = options;
1797
        options = {};
1798
    }
1799
1800
    feeStrategy = feeStrategy || Wallet.FEE_STRATEGY_OPTIMAL;
1801
    options = options || {};
1802
1803
    var params = {
1804
        outputs: options.outputs ? options.outputs : 1,
1805
        zeroconf: allowZeroConf ? 1 : 0,
1806
        zeroconfself: (typeof options.allowZeroConfSelf !== "undefined" ? options.allowZeroConfSelf : true) ? 1 : 0,
1807
        fee_strategy: feeStrategy
1808
    };
1809
1810
    if (options.forcefee) {
1811
        params['forcefee'] = options.forcefee;
1812
    }
1813
1814
    return self.client.get("/wallet/" + identifier + "/max-spendable", params, true, cb);
1815
};
1816
1817
/**
1818
 * get all UTXOs for an wallet (paginated)
1819
 *
1820
 * @param identifier    string      wallet identifier
1821
 * @param [params]      object      pagination: {page: 1, limit: 20, sort_dir: 'asc'}
1822
 * @param [cb]          function    callback function to call when request is complete
1823
 * @return q.Promise
1824
 */
1825
APIClient.prototype.walletUTXOs = function(identifier, params, cb) {
1826
    var self = this;
1827
1828
    if (typeof params === "function") {
1829
        cb = params;
1830
        params = null;
1831
    }
1832
1833
    return self.client.get("/wallet/" + identifier + "/utxos", params, true, cb);
1834
};
1835
1836
/**
1837
 * get a paginated list of all wallets associated with the api user
1838
 *
1839
 * @param [params]      object      pagination: {page: 1, limit: 20}
1840
 * @param [cb]          function    callback function to call when request is complete
1841
 * @return q.Promise
1842
 */
1843
APIClient.prototype.allWallets = function(params, cb) {
1844
    var self = this;
1845
1846
    if (typeof params === "function") {
1847
        cb = params;
1848
        params = null;
1849
    }
1850
1851
    return self.client.get("/wallets", params, true, cb);
1852
};
1853
1854
/**
1855
 * verify a message signed bitcoin-core style
1856
 *
1857
 * @param message        string
1858
 * @param address        string
1859
 * @param signature      string
1860
 * @param [cb]          function    callback function to call when request is complete
1861
 * @return q.Promise
1862
 */
1863
APIClient.prototype.verifyMessage = function(message, address, signature, cb) {
1864
    var self = this;
1865
1866
    // we could also use the API instead of the using bitcoinjs-lib to verify
1867
    // return self.client.post("/verify_message", null, {message: message, address: address, signature: signature}, cb);
1868
1869
    var deferred = q.defer();
1870
    deferred.promise.nodeify(cb);
1871
    try {
1872
        var result = bitcoinMessage.verify(address, self.network.messagePrefix, message, new Buffer(signature, 'base64'));
0 ignored issues
show
Bug introduced by
The variable Buffer seems to be never declared. If this is a global, consider adding a /** global: Buffer */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1873
        deferred.resolve(result);
1874
    } catch (e) {
1875
        deferred.reject(e);
1876
    }
1877
1878
    return deferred.promise;
1879
};
1880
1881
/**
1882
 * max is 0.001
1883
 * testnet only
1884
 *
1885
 * @param address
1886
 * @param amount
1887
 * @param cb
1888
 */
1889
APIClient.prototype.faucetWithdrawl = function(address, amount, cb) {
1890
    var self = this;
1891
1892
    return self.client.post("/faucet/withdrawl", null, {address: address, amount: amount}, cb);
1893
};
1894
1895
/**
1896
 * send a raw transaction
1897
 *
1898
 * @param rawTransaction    string      raw transaction as HEX
1899
 * @param [cb]              function    callback function to call when request is complete
1900
 * @return q.Promise
1901
 */
1902
APIClient.prototype.sendRawTransaction = function(rawTransaction, cb) {
1903
    var self = this;
1904
1905
    return self.client.post("/send-raw-tx", null, rawTransaction, cb);
1906
};
1907
1908
/**
1909
 * get the current price index
1910
 *
1911
 * @param [cb]          function    callback({'USD': 287.30})
1912
 * @return q.Promise
1913
 */
1914
APIClient.prototype.price = function(cb) {
1915
    var self = this;
1916
1917
    return self.client.get("/price", null, false, cb);
1918
};
1919
1920
module.exports = APIClient;
1921